home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / faxd / Class2.c++ < prev    next >
C/C++ Source or Header  |  1994-08-01  |  19KB  |  648 lines

  1. /*    $Header: /usr/people/sam/fax/faxd/RCS/Class2.c++,v 1.78 1994/04/26 17:37:34 sam Rel $ */
  2. /*
  3.  * Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
  4.  * Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and 
  7.  * its documentation for any purpose is hereby granted without fee, provided
  8.  * that (i) the above copyright notices and this permission notice appear in
  9.  * all copies of the software and related documentation, and (ii) the names of
  10.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  11.  * publicity relating to the software without the specific, prior written
  12.  * permission of Sam Leffler and Silicon Graphics.
  13.  * 
  14.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  15.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  16.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  17.  * 
  18.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23.  * OF THIS SOFTWARE.
  24.  */
  25. #include "Class2.h"
  26. #include "ModemConfig.h"
  27.  
  28. #include <stdlib.h>
  29. #include <ctype.h>
  30.  
  31. Class2Modem::Class2Modem(FaxServer& s, const ModemConfig& c) : FaxModem(s,c)
  32. {
  33.     hangupCode[0] = '\0';
  34.     group3opts = 0;
  35.     serviceType = 0;            // must be set in derived class
  36. }
  37.  
  38. Class2Modem::~Class2Modem()
  39. {
  40. }
  41.  
  42. void
  43. Class2Modem::setupDefault(fxStr& s, const fxStr& configured, const char* def)
  44. {
  45.     if (configured == "")
  46.     s = def;
  47.     else
  48.     s = configured;
  49. }
  50.  
  51. /*
  52.  * Check if the modem is a Class 2 modem and, if so,
  53.  * configure it for use.  We try to confine a lot of
  54.  * the manufacturer-specific bogosity here and leave
  55.  * the remainder of the Class 2 support fairly generic.
  56.  */
  57. fxBool
  58. Class2Modem::setupModem()
  59. {
  60.     if (!selectBaudRate(conf.maxRate, conf.flowControl, conf.flowControl))
  61.     return (FALSE);
  62.     // Query service support information
  63.     if (atQuery("+FCLASS=?", modemServices, 500))
  64.     traceBits(modemServices & SERVICE_ALL, serviceNames);
  65.     if ((modemServices & serviceType) == 0)
  66.     return (FALSE);
  67.     atCmd(classCmd);
  68.  
  69.     /*
  70.      * Query manufacturer, model, and firmware revision.
  71.      * We use the manufacturer especially as a key to
  72.      * working around firmware bugs (yech!).
  73.      */
  74.     if (setupManufacturer(modemMfr)) {
  75.     modemCapability("Mfr \"%s\"", (char*) modemMfr);
  76.     modemMfr.raisecase();
  77.     }
  78.     (void) setupModel(modemModel);
  79.     (void) setupRevision(modemRevision);
  80.     if (modemModel != "")
  81.     modemCapability("Model \"%s\"", (char*) modemModel);
  82.     if (modemRevision != "")
  83.     modemCapability("Revision \"%s\"", (char*) modemRevision);
  84.  
  85.     /*
  86.      * Get modem capabilities and calculate best signalling
  87.      * rate, data formatting capabilities, etc. for use in
  88.      * T.30 negotiations.
  89.      */
  90.     fxStr t30parms;
  91.     if (!doQuery(dccQueryCmd, t30parms, 500)) {
  92.     serverTrace("Error getting modem capabilities");
  93.     return (FALSE);
  94.     }
  95.     /*
  96.      * Syntax: (vr),(br),(wd),(ln),(df),(ec),(bf),(st)
  97.      * where,
  98.      *    vr    vertical resolution
  99.      *    br    bit rate
  100.      *    wd    page width
  101.      *    ln    page length
  102.      *    df    data compression
  103.      *    ec    error correction
  104.      *    bf    binary file transfer
  105.      *    st    scan time/line
  106.      */
  107.     if (!parseRange(t30parms, modemParams)) {
  108.     serverTrace("Error parsing \"%s\" query response: \"%s\"",
  109.         (char*) dccQueryCmd, (char*) t30parms);
  110.     return (FALSE);
  111.     }
  112.     traceModemParams();
  113.     /*
  114.      * Check to see if the modem supports copy quality
  115.      * checking.  As of today, the only modem we know
  116.      * that does this is the Everex 24/96D.
  117.      */
  118.     cqCmds = "";
  119.     u_int modemCQ = 0;
  120.     if (atQuery("+FCQ=?", modemCQ)) {
  121.     if (modemCQ & (BIT(1)|BIT(2))) {
  122.         cqCmds = conf.class2CQCmd;
  123.         modemSupports("%s copy quality checking",
  124.         (modemCQ & BIT(2)) ? "1D+2D" : "1D");
  125.     } else
  126.         modemSupports("no copy quality checking");
  127.     }
  128.     /*
  129.      * Define the code to send to the modem to trigger the
  130.      * transfer of received Phase C data to the host.  Most
  131.      * modems based on SP-2388-A (Class 2) use DC1 while those
  132.      * based on SP-2388-B (Class 2.0) use DC2.  There are some
  133.      * exceptions (ZyXEL and modems based on Rockwell RC32ACL
  134.      * parts), but they are expected to set the appropriate
  135.      * value in the config file.
  136.      */
  137.     if (conf.class2RecvDataTrigger == "")
  138.     recvDataTrigger = (serviceType == SERVICE_CLASS2 ? DC1 : DC2);
  139.     else
  140.     recvDataTrigger = conf.class2RecvDataTrigger[0];
  141.     setupClass2Parameters();            // send parameters to the modem
  142.     return (TRUE);
  143. }
  144.  
  145. /*
  146.  * Send the modem the Class 2 configuration commands.
  147.  */
  148. fxBool
  149. Class2Modem::setupClass2Parameters()
  150. {
  151.     if (modemServices & serviceType) {        // when resetting at startup
  152.     atCmd(classCmd);
  153.     class2Cmd(tbcCmd);            // stream mode
  154.     class2Cmd(borCmd);            // Phase B+C bit order
  155.     class2Cmd(crCmd);            // enable receiving
  156.     /*
  157.      * Set Phase C data transfer timeout parameter.
  158.      * Note that we also have our own timeout parameter
  159.      * that should be at least as large (though it
  160.      * probably doesn't matter).  Some modem manufacturers
  161.      * do not support this command (or perhaps they
  162.      * hide it under a different name).
  163.      */
  164.     class2Cmd(phctoCmd);
  165.     /*
  166.      * Try to setup byte-alignment of received EOL's.
  167.      * As always, this is problematic.  If the modem
  168.      * does not support this, but accepts the command
  169.      * (as many do!), then received facsimile will be
  170.      * incorrectly tagged as having byte-aligned EOL
  171.      * codes in them--not usually much of a problem.
  172.      */
  173.     if (conf.class2RELCmd != "" && class2Cmd(conf.class2RELCmd))
  174.         group3opts |= GROUP3OPT_FILLBITS;
  175.     else
  176.         group3opts &= ~GROUP3OPT_FILLBITS;
  177.     /*
  178.      * Setup various other parameters if defined for modem.
  179.      */
  180.     if (cqCmds != "")            // copy quality checking
  181.         (void) class2Cmd(cqCmds);
  182.     if (nrCmd != "")            // negotiation reporting
  183.         (void) class2Cmd(nrCmd);
  184.     if (pieCmd != "")            // program interrupt enable
  185.         (void) class2Cmd(pieCmd);
  186.     if (getHDLCTracing() && bugCmd != "")    // HDLC frame tracing
  187.         class2Cmd(bugCmd);
  188.     /*
  189.      * Force the DCC so that we override whatever
  190.      * the modem defaults are.
  191.      */
  192.     setupDCC();
  193.     /*
  194.      * Enable adaptive-answer support.  If we're configured,
  195.      * we'll act like getty and initiate a login session if
  196.      * we get a data connection.  Note that we do this last
  197.      * so that the modem can be left in a state other than
  198.      * +FCLASS=2 (e.g. Rockwell-based modems often need to be
  199.      * in Class 0).
  200.      */
  201.     if (conf.setupAACmd != "")
  202.         (void) atCmd(conf.setupAACmd);
  203.     }
  204.     return (TRUE);
  205. }
  206.  
  207. /*
  208.  * Setup DCC to reflect best capabilities of the server.
  209.  */
  210. fxBool
  211. Class2Modem::setupDCC()
  212. {
  213.     params.vr = getBestVRes();
  214.     params.br = getBestSignallingRate();
  215.     params.wd = getBestPageWidth();
  216.     params.ln = getBestPageLength();
  217.     params.df = getBestDataFormat();
  218.     params.ec = EC_DISABLE;        // XXX
  219.     params.bf = BF_DISABLE;
  220.     params.st = getBestScanlineTime();
  221.     return class2Cmd(dccCmd, params);
  222. }
  223.  
  224. /*
  225.  * Parse a ``capabilities'' string from the modem and
  226.  * return the values through the params parameter.
  227.  */
  228. fxBool
  229. Class2Modem::parseClass2Capabilities(const char* cap, Class2Params& params)
  230. {
  231.     int n = sscanf(cap, "%d,%d,%d,%d,%d,%d,%d,%d",
  232.     ¶ms.vr, ¶ms.br, ¶ms.wd, ¶ms.ln,
  233.     ¶ms.df, ¶ms.ec, ¶ms.bf, ¶ms.st);
  234.     if (n == 8) {
  235.     /*
  236.      * Clamp values to insure modem doesn't feed us
  237.      * nonsense; should log bogus stuff also.
  238.      */
  239.     params.vr = fxmin(params.vr, (u_int) VR_FINE);
  240.     params.br = fxmin(params.br, (u_int) BR_14400);
  241.     params.wd = fxmin(params.wd, (u_int) WD_864);
  242.     params.ln = fxmin(params.ln, (u_int) LN_INF);
  243.     params.df = fxmin(params.df, (u_int) DF_2DMMR);
  244.     if (params.ec > EC_ENABLE)
  245.         params.ec = EC_DISABLE;
  246.     if (params.bf > BF_ENABLE)
  247.         params.bf = BF_DISABLE;
  248.     params.st = fxmin(params.st, (u_int) ST_40MS);
  249.     return (TRUE);
  250.     } else {
  251.     protoTrace("MODEM protocol botch, can not parse \"%s\"", cap);
  252.     return (FALSE);
  253.     }
  254. }
  255.  
  256. /*
  257.  * Set the modem into data service after auto-detecting
  258.  * an incoming data connection.  Class 2 says this should
  259.  * be automatically done for us.
  260.  */
  261. fxBool
  262. Class2Modem::dataService()
  263. {
  264.     return (TRUE);                // nothing to do
  265. }
  266.  
  267. /*
  268.  * Set the modem into voice service after auto-detecting
  269.  * an incoming voice call.  ZyXEL (only modem that supports
  270.  * this) say that it should be done automatically.
  271.  */
  272. fxBool
  273. Class2Modem::voiceService()
  274. {
  275.     return (TRUE);
  276. }
  277.  
  278. fxBool
  279. Class2Modem::setupRevision(fxStr& revision)
  280. {
  281.     if (FaxModem::setupRevision(revision)) {
  282.     /*
  283.      * Cleanup ZyXEL response (argh), modem gives:
  284.      * +FREV?    "U1496E   V 5.02 M    "
  285.      */
  286.     if (modemMfr == "ZYXEL") {
  287.         u_int pos = modemRevision.next(0, ' ');
  288.         if (pos != modemRevision.length()) {    // rev. has model 1st
  289.         pos = modemRevision.skip(pos, ' ');
  290.         modemRevision.remove(0, pos);
  291.         }
  292.     }
  293.     return (TRUE);
  294.     } else
  295.     return (FALSE);
  296. }
  297.  
  298. fxBool
  299. Class2Modem::setupModel(fxStr& model)
  300. {
  301.     if (FaxModem::setupModel(model)) {
  302.     /*
  303.      * Cleanup ZyXEL response (argh), modem gives:
  304.      * +FMDL?    "U1496E   V 5.02 M    "
  305.      */
  306.     if (modemMfr == "ZYXEL")
  307.         modemModel.resize(modemModel.next(0, ' '));    // model is first word
  308.     return (TRUE);
  309.     } else
  310.     return (FALSE);
  311. }
  312.  
  313. /*
  314.  * Strip any quote marks from a string.  This
  315.  * is used for received TSI+CSI strings passed
  316.  * to the server.
  317.  */
  318. fxStr
  319. Class2Modem::stripQuotes(const char* cp)
  320. {
  321.     fxStr s(cp);
  322.     u_int pos = s.next(0, '"');
  323.     if (pos != s.length())
  324.     s.remove(0,pos+1);
  325.     pos = s.next(0, '"');
  326.     if (pos != s.length())
  327.     s.remove(pos, s.length()-pos);
  328.     return (s);
  329. }
  330.  
  331. /*
  332.  * Construct the Calling Station Identifier (CSI) string
  333.  * for the modem.  This is encoded as an ASCII string
  334.  * according to Table 3/T.30 (see the spec).  Hyphen ('-')
  335.  * and period are converted to space; otherwise invalid
  336.  * characters are ignored in the conversion.  The string may
  337.  * be at most 20 characters (according to the spec).
  338.  */
  339. void
  340. Class2Modem::setLID(const fxStr& number)
  341. {
  342.     fxStr csi;
  343.     u_int n = fxmin((u_int) number.length(), (u_int) 20);
  344.     for (u_int i = 0; i < n; i++) {
  345.     char c = number[i];
  346.     if (c == ' ' || c == '-' || c == '.')
  347.         csi.append(' ');
  348.     else if (c == '+' || isdigit(c))
  349.         csi.append(c);
  350.     }
  351.     class2Cmd(lidCmd, (char*) csi);
  352. }
  353.  
  354. /* 
  355.  * Modem manipulation support.
  356.  */
  357.  
  358. /*
  359.  * Reset a Class 2 modem.
  360.  */
  361. fxBool
  362. Class2Modem::reset(long ms)
  363. {
  364.     return (FaxModem::reset(ms) && setupClass2Parameters());
  365. }
  366.  
  367. /*
  368.  * Abort an active Class 2 session.
  369.  */
  370. fxBool
  371. Class2Modem::abort(long)
  372. {
  373.     return (class2Cmd(abortCmd));
  374. }
  375.  
  376. /*
  377.  * Wait (carefully) for some response from the modem.
  378.  * In particular, beware of unsolicited hangup messages
  379.  * from the modem.  Some modems seem to not use the
  380.  * Class 2 required +FHNG response--and instead give
  381.  * us an unsolicited NO CARRIER message.  Isn't life
  382.  * wondeful?
  383.  */
  384. fxBool
  385. Class2Modem::waitFor(ATResponse wanted, long ms)
  386. {
  387.     for (;;) {
  388.     ATResponse response = atResponse(rbuf, ms);
  389.     if (response == wanted)
  390.         return (TRUE);
  391.     switch (response) {
  392.     case AT_TIMEOUT:
  393.     case AT_EMPTYLINE:
  394.     case AT_ERROR:
  395.     case AT_NOCARRIER:
  396.     case AT_NODIALTONE:
  397.     case AT_NOANSWER:
  398.         modemTrace("ERROR: %s", ATresponses[response]);
  399.         return (FALSE);
  400.     case AT_FHNG:
  401.         // return hangup status, but try to wait for requested response
  402.         { char buf[1024]; (void) atResponse(buf, 2*1000); }
  403.         return (isNormalHangup());
  404.     }
  405.     }
  406. }
  407.  
  408. /*
  409.  * Interfaces for sending a Class 2 command; i.e, AT+F<cmd>
  410.  */
  411.  
  412. /*
  413.  * Send <cmd> and wait for "OK"
  414.  */
  415. fxBool
  416. Class2Modem::class2Cmd(const char* cmd)
  417. {
  418.     return vatFaxCmd(AT_OK, "%s", cmd);
  419. }
  420.  
  421. /*
  422.  * Send <cmd>=<a0> and wait for "OK"
  423.  */
  424. fxBool
  425. Class2Modem::class2Cmd(const char* cmd, int a0)
  426. {
  427.     return vatFaxCmd(AT_OK, "%s=%u", cmd, a0);
  428. }
  429.  
  430. /*
  431.  * Send AT+F<cmd>=<t.30 parameters> and wait for "OK".
  432.  */
  433. fxBool
  434. Class2Modem::class2Cmd(const char* cmd, const Class2Params& p)
  435. {
  436.     const fxStr& params = p.cmd();
  437.     return vatFaxCmd(AT_OK, "%s=%s", cmd, (char*) params);
  438. }
  439.  
  440. /*
  441.  * Send AT+F<cmd>="<s>" and wait for "OK".
  442.  */
  443. fxBool
  444. Class2Modem::class2Cmd(const char* cmd, const char* s)
  445. {
  446.     return vatFaxCmd(AT_OK, "%s=\"%s\"", cmd, s);    // XXX handle escapes
  447. }
  448.  
  449. /*
  450.  * Parse a Class 2 parameter specification
  451.  * and return the resulting bit masks.
  452.  */
  453. fxBool
  454. Class2Modem::parseRange(const char* cp, Class2Params& p)
  455. {
  456.     if (!vparseRange(cp, 8, &p.vr,&p.br,&p.wd,&p.ln,&p.df,&p.ec,&p.bf,&p.st))
  457.     return (FALSE);
  458.     p.vr &= VR_ALL;
  459.     p.br &= BR_ALL;
  460.     p.wd &= WD_ALL;
  461.     p.ln &= LN_ALL;
  462.     p.df &= DF_ALL;
  463.     p.ec &= EC_ALL;
  464.     p.bf &= BF_ALL;
  465.     p.st &= ST_ALL;
  466.     return TRUE;
  467. }
  468.  
  469. const char*
  470. Class2Modem::skipStatus(const char* s)
  471. {
  472.     const char* cp;
  473.     for (cp = s; *cp != '\0' && *cp != ':'; cp++)
  474.     ;
  475.     return (*cp == ':' ? cp+1 : s);
  476. }
  477.  
  478. /*
  479.  * Hangup codes are broken up according to:
  480.  *   2388/89
  481.  *   2388/90 and 2388-A
  482.  *   2388-B
  483.  * The table to search is based on the modem type deduced
  484.  * at modem configuration time.
  485.  *
  486.  * Note that we keep the codes as strings to avoid having
  487.  * to distinguish the decimal numbers of 2388-A from the
  488.  * hexadecimal renumbering done in 2388-B!
  489.  */
  490. static struct HangupCode {
  491.     const char*    code[3];    // from 2388/89, 2388/90, 2388-A, and 2388-B
  492.     const char*    message;    // what code means
  493. } hangupCodes[] = {
  494. // Call placement and termination
  495.     {{  "0",  "0",  "0" }, "Normal and proper end of connection" },
  496.     {{  "1",  "1",  "1" }, "Ring detect without successful handshake" },
  497.     {{  "2",  "2",  "2" }, "Call aborted,  from +FK or <CAN>" },
  498.     {{ NULL,  "3",  "3" }, "No loop current" },
  499.     {{ NULL, NULL,  "4" }, "Ringback detected, no answer (timeout)" },
  500.     {{ NULL, NULL,  "5" }, "Ringback detected, no answer without CED" },
  501. // Transmit Phase A & miscellaneous errors
  502.     {{ "10", "10", "10" }, "Unspecified Phase A error" },
  503.     {{ "11", "11", "11" }, "No answer (T.30 T1 timeout)" },
  504. // Transmit Phase B
  505.     {{ "20", "20", "20" }, "Unspecified Transmit Phase B error" },
  506.     {{ "21", "21", "21" }, "Remote cannot be polled" },
  507.     {{ "22", "22", "22" }, "COMREC error in transmit Phase B/got DCN" },
  508.     {{ "23", "23", "23" }, "COMREC invalid command received/no DIS or DTC" },
  509.     {{ "24", "24", "24" }, "RSPREC error/got DCN" },
  510.     {{ "25", "25", "25" }, "DCS sent 3 times without response" },
  511.     {{ "26", "26", "26" }, "DIS/DTC received 3 times; DCS not recognized" },
  512.     {{ "27", "27", "27" }, "Failure to train at 2400 bps or +FMINSP value" },
  513.     {{ "28", "28", "28" }, "RSPREC invalid response received" },
  514. // Transmit Phase C
  515.     {{ "30", "40", "40" }, "Unspecified Transmit Phase C error" },
  516.     {{ NULL, NULL, "41" }, "Unspecified Image format error" },
  517.     {{ NULL, NULL, "42" }, "Image conversion error" },
  518.     {{ "33", "43", "43" }, "DTE to DCE data underflow" },
  519.     {{ NULL, NULL, "44" }, "Unrecognized Transparent data command" },
  520.     {{ NULL, NULL, "45" }, "Image error, line length wrong" },
  521.     {{ NULL, NULL, "46" }, "Image error, page length wrong" },
  522.     {{ NULL, NULL, "47" }, "Image error, wrong compression code" },
  523. // Transmit Phase D
  524.     {{ "40", "50", "50" }, "Unspecified Transmit Phase D error, including"
  525.                " +FPHCTO timeout between data and +FET command" },
  526.     {{ "41", "51", "51" }, "RSPREC error/got DCN" },
  527.     {{ "42", "52", "52" }, "No response to MPS repeated 3 times" },
  528.     {{ "43", "53", "53" }, "Invalid response to MPS" },
  529.     {{ "44", "54", "54" }, "No response to EOP repeated 3 times" },
  530.     {{ "45", "55", "55" }, "Invalid response to EOP" },
  531.     {{ "46", "56", "56" }, "No response to EOM repeated 3 times" },
  532.     {{ "47", "57", "57" }, "Invalid response to EOM" },
  533.     {{ "48", "58", "58" }, "Unable to continue after PIN or PIP" },
  534. // Received Phase B
  535.     {{ "50", "70", "70" }, "Unspecified Receive Phase B error" },
  536.     {{ "51", "71", "71" }, "RSPREC error/got DCN" },
  537.     {{ "52", "72", "72" }, "COMREC error" },
  538.     {{ "53", "73", "73" }, "T.30 T2 timeout, expected page not received" },
  539.     {{ "54", "74", "74" }, "T.30 T1 timeout after EOM received" },
  540. // Receive Phase C
  541.     {{ "60", "90", "90" }, "Unspecified Phase C error, including too much delay"
  542.                " between TCF and +FDR command" },
  543.     {{ "61", "91", "91" }, "Missing EOL after 5 seconds (section 3.2/T.4)" },
  544.     {{ "63", "93", "93" }, "DCE to DTE buffer overflow" },
  545.     {{ "64", "94", "92" }, "Bad CRC or frame (ECM or BFT modes)" },
  546. // Receive Phase D
  547.     {{ "70","100", "A0" }, "Unspecified Phase D error" },
  548.     {{ "71","101", "A1" }, "RSPREC invalid response received" },
  549.     {{ "72","102", "A2" }, "COMREC invalid response received" },
  550.     {{ "73","103", "A3" }, "Unable to continue after PIN or PIP, no PRI-Q" },
  551. // Everex proprietary error codes (9/28/90)
  552.     {{ NULL,"128", NULL }, "Cannot send: +FMINSP > remote's +FDIS(BR) code" },
  553.     {{ NULL,"129", NULL }, "Cannot send: remote is V.29 only,"
  554.                " local DCE constrained to 2400 or 4800 bps" },
  555.     {{ NULL,"130", NULL }, "Remote station cannot receive (DIS bit 10)" },
  556.     {{ NULL,"131", NULL }, "+FK aborted or <CAN> aborted" },
  557.     {{ NULL,"132", NULL }, "+Format conversion error in +FDT=DF,VR, WD,LN"
  558.                " Incompatible and inconvertable data format" },
  559.     {{ NULL,"133", NULL }, "Remote cannot receive" },
  560.     {{ NULL,"134", NULL }, "After +FDR, DCE waited more than 30 seconds for"
  561.                " XON from DTE after XOFF from DTE" },
  562.     {{ NULL,"135", NULL }, "In Polling Phase B, remote cannot be polled" },
  563. };
  564. #define    NCODES    (sizeof (hangupCodes) / sizeof (hangupCodes[0]))
  565.  
  566. /*
  567.  * Given a hangup code from a modem return a
  568.  * descriptive string.  We use strings here to
  569.  * avoid having to know what type of modem we're
  570.  * talking to (Rev A or Rev B or SP-2388).  This
  571.  * works right now becase the codes don't overlap
  572.  * (as strings).  Hopefully this'll continue to
  573.  * be true.
  574.  */
  575. const char*
  576. Class2Modem::hangupCause(const char* code)
  577. {
  578.     for (u_int i = 0; i < NCODES; i++) {
  579.     const HangupCode& c = hangupCodes[i];
  580.     if ((c.code[1] != NULL && strcasecmp(code, c.code[1]) == 0) ||
  581.         (c.code[2] != NULL && strcasecmp(code, c.code[2]) == 0))
  582.         return (c.message);
  583.     }
  584.     return ("Unknown code");
  585. }
  586.  
  587. /*
  588.  * Process a hangup code string.
  589.  */
  590. void
  591. Class2Modem::processHangup(const char* cp)
  592. {
  593.     while (isspace(*cp))            // strip leading white space
  594.     cp++;
  595.     while (*cp == '0' && cp[1] != '\0')        // strip leading 0's
  596.     cp++;
  597.     strncpy(hangupCode, cp, sizeof (hangupCode));
  598.     protoTrace("REMOTE HANGUP: %s (code %s)", hangupCause(hangupCode), hangupCode);
  599. }
  600.  
  601. /*
  602.  * Does the current hangup code string indicate
  603.  * that the remote side hung up the phone in a
  604.  * "normal and proper way".
  605.  */
  606. fxBool
  607. Class2Modem::isNormalHangup()
  608. {
  609.     // normal hangup is "", "0", or "00"
  610.     return (hangupCode[0] == '\0' ||
  611.     (hangupCode[0] == '0' &&
  612.      (hangupCode[1] == '0' || hangupCode[1] == '\0')));
  613. }
  614.  
  615. #include "t.30.h"
  616.  
  617. void
  618. Class2Modem::tracePPM(const char* dir, u_int ppm)
  619. {
  620.     static u_int ppm2fcf[8] = {
  621.     FCF_MPS,
  622.     FCF_EOM,
  623.     FCF_EOP,
  624.     0,
  625.     FCF_PRI_MPS,
  626.     FCF_PRI_EOM,
  627.     FCF_PRI_EOP,
  628.     0
  629.     };
  630.     FaxModem::tracePPM(dir, ppm2fcf[ppm&7]);
  631. }
  632.  
  633. void
  634. Class2Modem::tracePPR(const char* dir, u_int ppr)
  635. {
  636.     static u_int ppr2fcf[8] = {
  637.     0,
  638.     FCF_MCF,
  639.     FCF_RTN,
  640.     FCF_RTP,
  641.     FCF_PIN,
  642.     FCF_PIP,
  643.     0,
  644.     0
  645.     };
  646.     FaxModem::tracePPR(dir, ppr2fcf[ppr&7]);
  647. }
  648.